package com.yinkgh.datasource;

import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 数据源切面
 * @author yinkgh
 *
 */
@Component
@Aspect
@Order(-2000)
public class DataSourceAspect {
	
	private static Logger logger = Logger.getLogger(DataSourceAspect.class);

	@Pointcut("execution(* com.yinkgh.mybatis.service..*.*(..))")  
    private void anyMethod(){}//定义一个切入点 
	
	/**
	 * 拦截目标方法，获取由@DataSource指定的数据源标识，设置到线程存储中以便切换数据源
	 * 
	 * @param point
	 * @throws Exception
	 */
//	@Before("execution(* com.yinkgh.*.service.*.*(..))")
	@Around("anyMethod()")
	public Object intercept(ProceedingJoinPoint pjp) throws Exception {
		Class<?> target = pjp.getTarget().getClass();
		MethodSignature signature = (MethodSignature) pjp.getSignature();
		// 默认使用目标类型的注解，如果没有则使用其实现接口的注解
		for (Class<?> clazz : target.getInterfaces()) {
			resolveDataSource(clazz, signature.getMethod());
			logger.info("********************默认使用目标类型的注解：" +signature.getMethod().getName() );
		}
		resolveDataSource(target, signature.getMethod());
		logger.info("********************实现接口的注解：" +signature.getMethod().getName() );
		Object object = null;
		try {
			object = pjp.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		} finally {
			DataSourceContextHolder.clearDataSource();
		}
		return object;
	}

	/**
	 * 提取目标对象方法注解和类型注解中的数据源标识
	 * 
	 * @param clazz
	 * @param method
	 */
	private void resolveDataSource(Class<?> clazz, Method method) {
		try {
			Class<?>[] types = method.getParameterTypes();
			// 默认使用类型注解
			if (clazz.isAnnotationPresent(DataSource.class)) {
				DataSource source = clazz.getAnnotation(DataSource.class);
				DataSourceContextHolder.clearDataSource();
				// 设置数据源
				DataSourceContextHolder.setDataSource(source.value());

				logger.info("********************类型注解设置数据源：" + source.value());
			}
			// 方法注解可以覆盖类型注解
			Method m = clazz.getMethod(method.getName(), types);
			if (m != null && m.isAnnotationPresent(DataSource.class)) {
				DataSource source = m.getAnnotation(DataSource.class);
				DataSourceContextHolder.clearDataSource();
				// 设置数据源
				DataSourceContextHolder.setDataSource(source.value());
				logger.info("********************方法注解设置数据源：" + source.value());
			}
		} catch (Exception e) {
			logger.info(clazz + ":" + e.getMessage());
		}
	}

}
